package com.apes.fn.fico.reimbursement.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.fico.reimbursement.model.ExpenseDebit;
import com.apes.fn.fico.reimbursement.model.ExpenseDebitItem;
import com.apes.fn.fico.reimbursement.repository.ExpenseDebitDao;
import com.apes.fn.fico.reimbursement.repository.ExpenseDebitRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.Arith;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.Tools;
import com.apes.hr.base.model.Employee;
import com.apes.hr.base.repository.EmployeeRepository;
import com.apes.hr.base.repository.WorkPostRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.rbac.model.Post;
import com.apes.scm.rbac.model.User;
import com.apes.scm.rbac.repository.PostRepository;
import com.apes.scm.rbac.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
/**
 * 费用借支
 * Created by lishikun on 2021/1/27 11:26
 */
@Service("expenseDebitService")
public class ExpenseDebitService extends DomainService {
    @Autowired
    ExpenseDebitRepository expenseDebitRepository;

    @Autowired
    PostRepository postRepository;

    @Autowired
    ReimbursementService reimbursementService;

    /**
     * 创建
     *
     * @param request
     * @return
     */
    public ExpenseDebit create(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        //按需求所说 岗位为空时取操作员的人员档案中的岗位
        Optional.ofNullable(request.getPerson()).ifPresent(person -> {
            Employee employee = SpringManager.getBean(EmployeeRepository.class).findByParty(((PartyRole) person).getParty());
            if (!Tools.isNull(employee)) {
                expenseDebit.setWorkPost(employee.getPost());
            }
        });
        expenseDebit.computeAmt();
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    public ExpenseDebit update(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebit.computeAmt();
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    public String delete(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebitRepository.delete(expenseDebit);
        return "删除成功";
    }

    // 出纳
    public void cashier(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
//        expenseDebit.setState("cashier");
        expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    /**
     * 会计审核
     *
     * @param request
     */
    public void accountingAudit(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebit.computeAmt();
        expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    /**
     * 付款
     *
     * @param request
     */
    public ExpenseDebit pay(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        if (Objects.isNull(expenseDebit.getUpload()))
            throw new RuntimeException("付款回执文件没有上传");
//        expenseDebit.getTransactionFlowItems().forEach(transactionFlowItem -> {
//            double amount = Arith.add(transactionFlowItem.getPayAmount(), transactionFlowItem.getPaymentAmount());
//            if (amount > transactionFlowItem.getAmount()) {
//                throw new RuntimeException("付款金额不能大于借支金额");
//            }
//            transactionFlowItem.setPayAmount(amount);
//        });

        //减少原单的使用额
        useAmountMethod(expenseDebitRepository.findOne(expenseDebit.getId()), -1);
        //增加使用额
        useAmountMethod(expenseDebit, 1);


        expenseDebit.computeAmt();
        //检查终止金额
        if (Arith.sub(expenseDebit.getExpenseDebitSum(), expenseDebit.getPayAmount()) < expenseDebit.getExpenseDebitItems().stream().map(ExpenseDebitItem::getCancelAmount).reduce(Arith::add).orElse(0.0)) {
            throw new RuntimeException("终止总金额超过可终止金额(借支总金额-付款金额)！请检查后再终止！");
        }
        if (Arith.add(expenseDebit.getPayAmount(), expenseDebit.getExpenseDebitItems().stream().map(ExpenseDebitItem::getCancelAmount).reduce(Arith::add).orElse(0.0)) == expenseDebit.getExpenseDebitSum()) {
            expenseDebit.setState("repayment");
        }

        expenseDebit.setPayId(this.getPerson(request));
        expenseDebit.setPayDate(new Date());
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    /**
     * 银行流水使用额
     *
     * @param expenseDebit
     */
    private void useAmountMethod(ExpenseDebit expenseDebit, int flag) {
//        //增加银行流水使用额
//        expenseDebit.getTransactionFlowItems().forEach(transactionFlowItem -> {
//            TransactionFlow transactionFlow = transactionFlowItem.getTransactionFlow();
//            transactionFlow.computeAmt(transactionFlowItem.getPaymentAmount() * flag);
//            SpringManager.getBean(TransactionFlowRepository.class).saveAndFlush(transactionFlow);
//        });
    }

    public ExpenseDebit approve(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebit.setState("approve");
        expenseDebit.setApproveId(this.getPerson(request));// 设置审核人
        expenseDebit.setApproveDate(new Date()); // 设置审核时间
        // 设置发起人
        String creatorId = expenseDebit.getCreator();
        setSupperOA(expenseDebit);
        expenseDebit.setRemark(expenseDebit.getExpenseDebitItems().stream()
                .filter(ReimbursementService.distinctByKey(expenseDebitItem ->expenseDebitItem.getDutyCenter().getId()))
                .map(expenseDebitItem -> Optional.ofNullable((String) invoke("fn.expenseDebit.findAllByCostCenter",
                        MapUtil.mapper("data", MapUtil.mapper("id", expenseDebitItem.getDutyCenter().getId())))).orElse(null))
                .filter(Objects::nonNull).collect(Collectors.joining(";")));
        expenseDebit.setOASponsor(SpringManager.getBean(UserRepository.class).findAll(JpaDsl.toCriteriaByEq("person.id", creatorId)).get(0).getId().toString());
        ExpenseDebit debit = expenseDebitRepository.saveAndFlush(expenseDebit);
        //设置各类费用审批主管并检验费用类型
        inspectionFeeType(debit, debit.getFeeType().getCode());
        return debit;
    }

    private void setSupperOA(ExpenseDebit expenseDebit) {
        // 控制父类审批
        String id = SpringManager.getBean(WorkPostRepository.class).findOne(expenseDebit.getWorkPost().getId()).getParents().getId();
        String workPostId = expenseDebit.getWorkPost().getId();
        if ("0000016".equals(workPostId)) {
            expenseDebit.setNeedSupperOA("1");
            return;
        }
        if ("0000187".equals(id) && "DecorationCost".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");     // 上级岗位
        else if ("0000095".equals(id) && "FinanceCost".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000105".equals(id) && "BusinessExpenses".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000080".equals(id) && "UnionType".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000172".equals(id) && "CustomerFee".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000115".equals(id) && "InfoCost".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000044".equals(id) && "PurchasingCost1".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000043".equals(id) && "Logistics".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000027".equals(id) && "DoorPipe".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000109".equals(id) && "DataType".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000140".equals(id) && "OperationsCenter".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000191".equals(id) && "Burst".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else if ("0000822".equals(id) && "Training".equals(expenseDebit.getFeeType().getCode()))
            expenseDebit.setNeedSupperOA("1");
        else expenseDebit.setNeedSupperOA("0");
    }

    /**
     * 检验费用类型
     *
     * @param expenseDebit
     * @param feeTypeCode
     */
    private void inspectionFeeType(ExpenseDebit expenseDebit, String feeTypeCode) {
        switch (feeTypeCode) {
            //信息类费用
            case "InfoCost":
                assignmentCandidates(expenseDebit, "0000115");
                break;

            //商务类费用
            case "BusinessExpenses":
                assignmentCandidates(expenseDebit, "0000105");
                break;

            //客服类费用
            case "CustomerFee":
                assignmentCandidates(expenseDebit, "0000172");
                break;

            //采购类费用(外协不得垫付后报销)
            case "PurchasingCost1":
                assignmentCandidates(expenseDebit, "0000044");
                break;

            //采购类（设备维修）费用
            case "PurchasingCost2":
                expenseDebit.setCandidates(JSON.toJSONString(Arrays.asList("305", "958")));
                break;

            //装修类费用
            case "DecorationCost":
                assignmentCandidates(expenseDebit, "0000187");
                break;

            //财务类费用
            case "FinanceCost":
                assignmentCandidates(expenseDebit, "0000095");
                break;

            //行政类费用
            case "Administrative":
                //工会类费用
            case "UnionType":
                assignmentCandidates(expenseDebit, "0001072");
                break;

            //物流类费用
            case "Logistics":
                assignmentCandidates(expenseDebit, "0000043");
                break;

            //门管技术类费用
            case "DoorPipe":
                assignmentCandidates(expenseDebit, "0000027");
                break;

            //数据类费用
            case "DataType":
                assignmentCandidates(expenseDebit, "0000109");
                break;

            //市场中心类
            case "Market":

                //运营中心费用
            case "OperationsCenter":
                assignmentCandidates(expenseDebit, "0000140");
                break;

            //连发类费用
            case "Burst":
                assignmentCandidates(expenseDebit, "0000079");
                break;

            //培训类费用
            case "Training":
                assignmentCandidates(expenseDebit, "0000822");
                break;

            //人资类费用
            case "HumanResources":
                assignmentCandidates(expenseDebit, "0001232");
                break;

            //证券法务类费用
            case "SecuritiesLegalAffairs":
                assignmentCandidates(expenseDebit, "0001229");
                break;

            //SAAS研发类费用
            case "S006500":
                assignmentCandidates(expenseDebit, "0000140");
                break;
            //品牌部费用
            case "BrandType":
                assignmentCandidates(expenseDebit, "0001272");
                break;
            default:
                throw new RuntimeException("当前费用类型未定义待办岗位！无法发起OA");
        }
    }

    /**
     * 设置各类费用审批主管
     *
     * @param expenseDebit
     * @param postId
     */
    private void assignmentCandidates(ExpenseDebit expenseDebit, String postId) {
        List<Post> posts = postRepository.findAllByWorkPostId(postId);
        List<User> agents = new ArrayList<>();
        if (!posts.isEmpty()) {
            posts.forEach(post -> {
                List<User> users = SpringManager.getBean(UserRepository.class).findByPost(post.getId());
                if (!users.isEmpty()) agents.addAll(users);
            });
        }
        if (!agents.isEmpty()) {
            List<String> users = new ArrayList<>();
            agents.stream().distinct().forEach(agent -> {
                users.add(String.valueOf(agent.getId()));
            });
            expenseDebit.setCandidates(JSONObject.toJSONString(users));
        }
    }

    // 终止
    public ExpenseDebit stop(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        Double cancelAmount = expenseDebit.getExpenseDebitItems().stream().map(ExpenseDebitItem::getCancelAmount).reduce(Arith::add).orElse(0.0);
        if (Arith.add(expenseDebit.getPayAmount(), cancelAmount) == expenseDebit.getExpenseDebitSum()) {
            expenseDebit.setState("repayment");
        }
        if (cancelAmount == expenseDebit.getExpenseDebitSum()) {
            expenseDebit.setState("repeal");

        }
        expenseDebit.setRepealId(this.getPerson(request));// 设置终止人
        expenseDebit.setRepealDate(new Date()); // 设置终止时间
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    // OA流程拒绝
    public ExpenseDebit refuse(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebit.setState("repeal");
        expenseDebit.setRepealId(this.getPerson(request));// 设置终止人
        expenseDebit.setRepealDate(new Date()); // 设置终止时间
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    // OA流程上传付款信息
    public ExpenseDebit upPaymentInfo(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        // 检测付款回执文件、银行流水号、付款账户填写情况
        if (Objects.isNull(expenseDebit.getUpload()))
            throw new RuntimeException("付款回执文件没有上传");
//        if (expenseDebit.getExpenseDebitItems().stream().filter(expenseDebitItem -> {
//            return expenseDebitItem.getTransactionFlowItems().size() == 0;}
//        ).count() > 0)
//            throw new RuntimeException("银行流水明细没有填写完毕！");
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    // 审核完成，待付款状态
    public ExpenseDebit finish(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        expenseDebit.setState("cashier");
        expenseDebit.computeAmt();
        expenseDebit.setRepaymentDate(new Date());
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    // 查询借支人所有待还款的借支单
    public Map findRepaymentByBorrower(SimpleRequest request) {
        JSONArray expenseDebits = request.getJO().getJSONObject("node").getJSONArray("expenseDebitSet");
        List<String> ids = new ArrayList<>();
        if (expenseDebits == null)
            return new HashMap();
        for (int i = 0; i < expenseDebits.size(); i++) {
            ids.add(expenseDebits.getJSONObject(i).getString("id"));
        }
        return MapUtil.mapper(
                "expenseDebits", expenseDebitRepository.findAllById(ids)
        );
    }
//
//    // 费用借支单还款
//    public ExpenseDebit repayment(SimpleRequest request) {
//        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
//        return expenseDebitRepository.saveAndFlush(expenseDebit);
//    }

    // 费用借支单退回
    public ExpenseDebit goBack(SimpleRequest request) {
        ExpenseDebit expenseDebit = request.getO(ExpenseDebit.class);
        if ("".equals(expenseDebit.getRemark()) || expenseDebit.getRemark() == null)
            throw new RuntimeException("请在备注上写明退回原因");
        expenseDebit.setState("create");
        return expenseDebitRepository.saveAndFlush(expenseDebit);
    }

    // 查询借支人（下拉用）
    public List<Map> findAllByBorrower(SimpleRequest request) {
        String borrower = request.getJO().getJSONObject("employee").getString("id");
        JSONArray jsonArray = SpringManager.getBean(ExpenseDebitDao.class).findAllByBorrower(borrower);
        return jsonArray.stream().map(
                item -> {
                    JSONObject jsonObject = (JSONObject) item;
//                    "borrower", jsonObject.getString("BORROWER"),
                    return MapUtil.mapper(
                            "reimbursementTotal", jsonObject.getDouble("EXPENSE_AMOUNT"),
                            "id", jsonObject.getString("ID"),
                            "repaymentMoney", jsonObject.getDouble("REPAYMENT_MONEY"));
                }).collect(Collectors.toList());
    }

    // 查询成本中心
    public String findAllByCostCenter(SimpleRequest request) {
        StringBuilder costCenterId = new StringBuilder(request.getJO().getJSONObject("data").getString("id"));
        JSONArray jsonArray = SpringManager.getBean(ExpenseDebitDao.class).findAllByCostCenter(costCenterId.toString());
        costCenterId = new StringBuilder();
        if (jsonArray.size() > 0) {
            costCenterId.append("存在三月未冲账借支单 ");
            for (Object o : jsonArray) {
                JSONObject json = (JSONObject) o;
                Calendar c = Calendar.getInstance();
                c.setTime(json.getDate("REPAYMENT_DATE"));
                costCenterId.append("单号： ").append(json.getString("ID")).append(", 时间：").append(c.get(Calendar.YEAR)).append("年").append(c.get(Calendar.MONTH) + 1).append("月").append(c.get(Calendar.DAY_OF_MONTH)).append("日 ");
            }
        }
        return Tools.isNull(costCenterId.toString())?null:costCenterId.toString();
    }

    /**
     * 根据借支人查
     *
     * @param request
     * @return
     */
    public Map findByBorrower(SimpleRequest request) {
        String borrowerId = request.get("borrowerId");
        Page<Map> page = expenseDebitRepository.findByBorrower(borrowerId, PageRequest.of(request.get("page"), request.get("size")));
        Map mapper = MapUtil.mapped(page);
        if (page.getContent().size() == 0) {
            mapper.put("msg", "未找到历史数据！");
        }
        return mapper;

    }

}
